package com.rr.util;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * 本地缓存工具类
 * @Author yangtiancheng
 * @Date 2023/3/22 19:44
**/
@Component
@Slf4j
public class LocalCacheHelper {
    final static Map<String, LoadingCache<String, ?>> loadingCacheMap = new ConcurrentHashMap<>();

    public <V> LoadingCache<String, V> create(int initialCapacity, int maximumSize, long expire, @NotNull CacheLoader<String, V> loader) {
       return CacheBuilder.newBuilder()
                .initialCapacity(initialCapacity)
                .maximumSize(maximumSize)
                .expireAfterWrite(expire, TimeUnit.SECONDS)
                .build(loader);
    }
    

    public <V> V get(String instanceName, String cacheKey){
        if(!loadingCacheMap.containsKey(instanceName)){
            return null;
        }

        try {
            return (V) loadingCacheMap.get(instanceName).get(cacheKey);
        } catch (Exception e) {
            log.error("本地缓存获取失败", e);
            return null;
        }
    }

    /**
     * 获取缓存数据（自动加载）
     * @param instanceName 缓存种类名
     * @param cacheKey 缓存key
     * @param expire 缓存时长， 单位：秒
     * @param loader 数据加载器
     * @return
     * @param <V>
     */
    public <V> V get(String instanceName, String cacheKey, long expire, @NotNull Function<String, V> loader){
        try {
            LoadingCache loadingCache = getLoadingCacheInstance(instanceName, expire, loader);
            return  (V) loadingCache.get(cacheKey, () -> loader.apply(cacheKey));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public <V> boolean put(String instanceName, String cacheKey, V data, long expire){
        return put(instanceName, cacheKey, data, expire, (t)-> null);
    }

    /**
     * 放置缓存
     * @param instanceName 缓存实例名
     * @param cacheKey
     * @param data
     * @param expire 失效时间（缓存多久），单位：秒
     * @param loader 数据加载器
     */
    public <V> boolean put(String instanceName, String cacheKey, V data, long expire, @NotNull Function<String, V> loader){
        try {
            LoadingCache loadingCache = getLoadingCacheInstance(instanceName, expire, loader);
            loadingCache.put(cacheKey, data);
            return true;
        } catch (Exception e) {
            log.error("本地缓存放置失败", e);
            return false;
        }
    }

    private <V> LoadingCache getLoadingCacheInstance(String instanceName, long expire, Function<String, V> loader) {
        if(!loadingCacheMap.containsKey(instanceName)){
            synchronized (this){
                if(!loadingCacheMap.containsKey(instanceName)){
                    LoadingCache loadingCache = create(1, 10000, expire, new CacheLoader<String, V>(){
                        @Override
                        public V load(String key){
                            return loader.apply(key);
                        }
                    });
                    loadingCacheMap.put(instanceName, loadingCache);
                }
            }
        }
        return loadingCacheMap.get(instanceName);
    }

    public void invalidateAll(String instanceName) {
        LoadingCache<String, ?> stringLoadingCache = loadingCacheMap.get(instanceName);
        if (Objects.nonNull(stringLoadingCache)) {
            stringLoadingCache.invalidateAll();
        }
    }

}
